#ifndef VEC4_HPP
#define VEC4_HPP

#include "math.h"
#include "aiVector3D.h"
#include "Vec3.h"

template <typename T>
class VEC4
{
public:
    // direct variable access
    T    x, y, z, t;
    
    // initialization
    VEC4 ( );
    VEC4 (T a);
    VEC4 (T a, T b, T c, T d);
    template <typename U>
    VEC4 (const VEC4<U> &v);
    
    // operators
    VEC4 operator = (const VEC4 &v);              // assignment
    VEC4 operator + (const VEC4 &v) const;        // addition, by element
    VEC4 operator - (const VEC4 &v) const;        // subtraction, by element
    T    operator * (const VEC4 &v) const;        // dot product
    VEC4 operator * (const T &s) const;           // multiplication by a scalar
    VEC4 operator / (const T &s) const;           // division by a scalar
    VEC4 operator - () const;                     // negation
    T&   operator[] (const int i);                // access of members
    bool operator ==(const VEC4<T> &v) const;     // equality test
    
    // named operation functions
    T dot(const VEC4 &v) const;
    T length() const;
    T squareLength() const;
    VEC4 normalized () const;
    void normalize();
    
};

// I REALLY DO HATE this... sigh

// initialization
template  <typename T>
VEC4<T>::VEC4 ( )
{
    x = (T)0.0;
    y = (T)0.0;
    z = (T)0.0;
    t = (T)0.0;
}

template  <typename T>
VEC4<T>::VEC4 (T a)
{
    x = a;
    y = a;
    z = a;
    t = a;
}

template  <typename T>
VEC4<T>::VEC4 (T a, T b, T c, T d)
{
    x = a;
    y = b;
    z = c;
    t = d;
}

template  <typename T>
template  <typename U>
VEC4<T>::VEC4 (const VEC4<U> &v)
{
    x = (T)v.x;
    y = (T)v.y;
    z = (T)v.z;
    t = (T)v.t;
}

// operators
template  <typename T>
VEC4<T> VEC4<T>::operator = (const VEC4<T> &v)            // assignment
{
    x = v.x;
    y = v.y;
    z = v.z;
    t = v.t;
    return (*this);
}

template  <typename T>
VEC4<T> VEC4<T>::operator + (const VEC4<T> &v) const        // addition, by element
{
    return VEC4(x + v.x, y + v.y, z + v.z, t + v.t);
}

template  <typename T>
VEC4<T> VEC4<T>::operator - (const VEC4<T> &v) const        // subtraction, by element
{
    return VEC4(x - v.x, y - v.y, z - v.z, t - v.t);
}

template  <typename T>
T    VEC4<T>::operator * (const VEC4<T> &v) const        // dot product
{
    return x * v.x + y * v.y + z * v.z + t * v.t;
}

template  <typename T>
VEC4<T> VEC4<T>::operator * (const T &s) const    // multiplication by a scalar
{
    return VEC4(x * s, y * s, z * s, t * s);
}

template  <typename T>
VEC4<T> VEC4<T>::operator / (const T &s) const    // division by a scalar
{
    return VEC4(x / s, y / s, z / s, t / s);
}

template  <typename T>
VEC4<T> VEC4<T>::operator - () const            // negation
{
    return VEC4(-x, -y, -z, -t);
}

template  <typename T>
T& VEC4<T>::operator[] (const int i)        // access if of members
{
    if (i <= 0)
        return &x;
    if (i == 1)
        return &y;
    if (i == 2)
        return &z;
    if (i >= 3)
        return &t;
}

template  <typename T>
bool VEC4<T>::operator ==(const VEC4<T> &v) const         // equality test
{
    return (x == v.x && y == v.y && z == v.z && t == v.t);
}


// named operation functions

template  <typename T>
T VEC4<T>::dot(const VEC4<T> &v) const
{
    return x * v.x + y * v.y + z * v.z + t * v.t;
}

template <typename T>
T VEC4<T>::length() const
{
    return (T)sqrt((double)x*x + (double)y*y + (double)z*z + (double)t*t);
}

template <typename T>
T VEC4<T>::squareLength() const
{
    return (T)((double)x*x + (double)y*y + (double)z*z + (double)t*t);
}

template  <typename T>
VEC4<T> VEC4<T>::normalized () const
{
    T len = this->length();
    if (len < 0.00001)
        return VEC4(0);
    return VEC4(x / len, y / len, z / len, t / len);
}

template  <typename T>
void VEC4<T>::normalize()
{
    T len = this->length();
    if (len < 0.00001)
        return;
    x = x / len;
    y = y / len;
    z = z / len;
    w = t / len;
}

#endif